<?php

namespace App\Service\WaiMai;

use App\Library\Address\AutoNaviProvinceCityArea;
use App\Library\Rms\RmsShopApi;
use App\Models\WaiMai\WaiMaiAnalysisModel;
use App\Service\BaseService;
use App\Service\OpenShop\SiteLocation\GuanyuanDataService;
use Ballen\Distical\Calculator;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
use Kuafu\File\FileUtil;
use Swoole\Coroutine\Channel;
use Swoole\Coroutine;

class WaiMaiAnalysisService extends BaseService
{
    /**
     * 默认距离 - 3km
     */
    const DEFAULT_DISTANCE = 3;

    /**
     * @var float 经纬度、距离
     */
    private float $lng, $lat, $distance;

    private int $sourceType = 0;

    private ?bool $isKf = null; // null全部 0否 1是

    private int $categoryType = WaiMaiAnalysisModel::CATEGORY_TYPE_DEFAULT;

    /**
     * 搜索附近的门店
     * @param float $lng
     * @param float $lat
     * @param float $distance
     */
    public function getNearByShops()
    {
        return $this->getNearByShopsBuilder()->addSelect(['orderNum', 'sourceType', 'shopCode', 'categoryType'])->get();
    }

    /**
     * 附近门店区分外卖排行榜
     * @param int $sourceType
     * @param int $categoryType
     * @param bool|null $isKf
     * @param int $limit
     */
    public function getNearBySourceTypeRank(int $sourceType, int $categoryType, ?bool $isKf, int $limit = 10)
    {
        return $this->getNearByShopsBuilder()
                    ->when($sourceType, function ($query) use ($sourceType) {
                        $query->where('sourceType', $sourceType);
                    })
                    ->when($categoryType, function ($query) use ($categoryType) {
                        $query->where('categoryType', $categoryType);
                    })
                    ->when($isKf !== null, function ($query) use ($isKf) {

                        if ($isKf) {
                            $query->where('shopCode', '>', 0);
                        } else {
                            $query->where('shopCode', 0);
                        }
                    })
                    ->addSelect('shopName', 'orderNum')
                    ->orderByDesc('orderNum')
                    ->limit($limit)
                    ->get()
                    ->map(function ($item) {
                        return [
                            'shopName' => $item->shopName,
                            'distance' => $item->distance,
                            'orderNum' => $item->orderNum,
                        ];
                    });
    }

    /**
     * 附近门店区分外卖
     * @return \Illuminate\Database\Eloquent\Builder[]|Collection|\Illuminate\Support\Collection
     */
    public function getNearByShop()
    {
        return $this->getNearByShopsBuilder()
                    ->addSelect('lat', 'lng', 'orderNum')
                    ->get()
                    ->map(function ($item) {
                        return [
                            'lat'      => $item->lat,
                            'lng'      => $item->lng,
                            'orderNum' => $item->orderNum,
                        ];
                    });
    }

    /**
     * 附近夸父门店排行榜
     * @param int $limit
     */
    public function getNearByKfShops(int $limit = 10)
    {
        $shops =  $this->getNearByShopsBuilder()
                    ->where('shopCode', '>', 0)
                    ->groupBy('shopCode')
                    ->addSelect('shopCode', 'shopName', DB::raw('sum(orderNum) as orderNum'))
                    ->orderByDesc('orderNum')
                    ->limit($limit)
                    ->get();

        $shopCodes = $shops->pluck('shopCode');

        // 单独查询美团、饿了么的销量
        $wmNums = WaiMaiAnalysisModel::query()->whereIn('shopCode', $shopCodes)->select('shopCode', 'sourceType', 'orderNum')->get()->toArray();

        $shops->map(function ($item) use ($wmNums) {

            foreach ($wmNums as $wmNum) {
                if ($item->shopCode == $wmNum['shopCode']) {

                    if ($wmNum['sourceType'] == RmsShopApi::SOURCE_TYPE_WM_MT) {
                        $item->mt = $wmNum['orderNum'];
                    } elseif ($wmNum['sourceType'] == RmsShopApi::SOURCE_TYPE_WM_ELM) {
                        $item->elm = $wmNum['orderNum'];
                    }
                }
            }

            $item->orderNum = (int)$item->orderNum;

            return $item;
        });

        return $shops;
    }


    /**
     * 设置经纬度
     * @param float $lng
     * @param float $lat
     * @param float $distance
     * @return $this
     * @throws \Exception
     */
    public function setLngLat(float $lng, float $lat, float $distance = self::DEFAULT_DISTANCE): self
    {
        $this->lng      = $lng;
        $this->lat      = $lat;
        $this->distance = $distance;

        if (!$this->lng || !$this->lat) {
            throw new \Exception('坐标不能为空', 400);
        }

        if ($this->distance <= 0) {
            throw new \Exception('距离不能为空', 400);
        }

        return $this;
    }

    /**
     * 计算外卖预估数据
     * @param float $shopLat
     * @param float $shopLng
     * @return array
     */
    public function calcEstimateData(): array
    {
        $elm = [
            'total'      => [],
            'total100'   => [],
            'kf100'      => [],
            'fried'      => [],
            'fried100'   => [],
            'friedkf100' => [],
        ];

        $mt = [
            'total'      => [],
            'total100'   => [],
            'kf100'      => [],
            'fried'      => [],
            'fried100'   => [],
            'friedkf100' => [],
        ];

        // 附近的全量外卖门店数据
        $nearbyShops = $this->getNearByShops();

        foreach ($nearbyShops as $nearbyShop) {
            $thirdOrderNum = $nearbyShop['orderNum'];
            $sourceType    = $nearbyShop['sourceType'];
            $shopCode      = $nearbyShop['shopCode'];
            $categoryType  = $nearbyShop['categoryType'];

            if ($sourceType == RmsShopApi::SOURCE_TYPE_WM_ELM) {
                $this->buildWmArr($elm, $thirdOrderNum, $shopCode, $categoryType);
            } elseif ($sourceType == RmsShopApi::SOURCE_TYPE_WM_MT) {
                $this->buildWmArr($mt, $thirdOrderNum, $shopCode, $categoryType);
            }
        }

        foreach ($elm as &$v) {
            $v = $this->calcNum($v);
        }

        foreach ($mt as &$v) {
            $v = $this->calcNum($v);
        }

        // 预估数据
        $mtEstimateData  = $this->convertDetailToResult(RmsShopApi::SOURCE_TYPE_WM_MT, $mt);
        $elmEstimateData = $this->convertDetailToResult(RmsShopApi::SOURCE_TYPE_WM_ELM, $elm);

        return [
            'estimate' => [
                'mt'  => $mtEstimateData,
                'elm' => $elmEstimateData,
            ],
            'detail'   => [
                'mt'  => $mt,
                'elm' => $elm,
            ]
        ];
    }

    /**
     * 附件的门店Builder
     * @return \Illuminate\Database\Eloquent\Builder
     */
    private function getNearByShopsBuilder()
    {
        $ranges = $this->getLatLngRange();

        return WaiMaiAnalysisModel::query()
                                  ->whereBetween('lat', [$ranges['min_lat'], $ranges['max_lat']])
                                  ->whereBetween('lng', [$ranges['min_lng'], $ranges['max_lng']])
                                  ->selectRaw("ST_Distance_Sphere(POINT(`lng`, `lat`), POINT(?, ?)) / 1000 AS distance",
                                      [$this->lng, $this->lat])
                                  ->having('distance', '<', $this->distance);
    }

    /**
     * 减少筛选范围
     * @param float $lng
     * @param float $lat
     * @param float $distance
     * @return float[]
     */
    private function getLatLngRange(): array
    {
        // 加上0.5公里，防止计算精度问题
        $distance = $this->distance + 0.5;

        $earth_radius = Calculator::MEAN_EARTH_RADIUS; // 地球半径，单位为公里

        // 计算纬度范围
        $dLatInRadians = $distance / $earth_radius;
        $minLat        = $this->lat - rad2deg($dLatInRadians);
        $maxLat        = $this->lat + rad2deg($dLatInRadians);

        // 计算经度范围
        $dLngInRadians = $distance / ($earth_radius * cos(deg2rad($this->lat)));
        $minLng        = $this->lng - rad2deg($dLngInRadians);
        $maxLng        = $this->lng + rad2deg($dLngInRadians);

        return [
            'min_lat' => $minLat,
            'max_lat' => $maxLat,
            'min_lng' => $minLng,
            'max_lng' => $maxLng
        ];
    }

    /**
     * 预估转换公式
     * @param int $sourceType
     * @param array $data
     * @return int
     */
    private function convertDetailToResult(int $sourceType, array $data): int
    {
        $res = 0;

        // 美团
        if ($sourceType == RmsShopApi::SOURCE_TYPE_WM_MT) {
            $res = 23.85 - 2.454 * $data['total'] + 0.534 * $data['total100'] + 0.791 * $data['kf100'] + 2.455 * $data['fried'] - 0.566 * $data['fried100'] - 0.566 * $data['friedkf100'];
        } elseif ($sourceType == RmsShopApi::SOURCE_TYPE_WM_ELM) {

            // 饿了么
            $res = 110.79 + 1.117 * $data['total'] - 0.595 * $data['total100'] + 0.707 * $data['kf100'] + 0.305 * $data['fried'] - 0.217 * $data['fried100'] - 0.217 * $data['friedkf100'];
        }

        // 非负直接返回
        if ($res > 0) {
            return (int)$res;
        }

        // 炸鸡炸串类的超过100
        if ($data['fried100'] > 0) {
            return (int)$data['fried100'];
        }

        // 全品类超过100
        if ($data['total100'] > 0) {
            return (int)$data['total100'];
        }

        return 0;
    }

    private function calcNum(array $arr): int
    {
        if (!$arr) {
            return 0;
        }

        return (int)(array_sum($arr) / count($arr));
    }

    private function buildWmArr(array &$arr, int $thirdOrderNum, int $shopCode, int $categoryType)
    {

        if ($shopCode == 0) {

            $arr['total'][] = $thirdOrderNum;

            if ($thirdOrderNum > 100) {
                $arr['total100'][] = $thirdOrderNum;
            }


            if ($categoryType == WaiMaiAnalysisModel::CATEGORY_TYPE_FRIED) {
                $arr['fried'][] = $thirdOrderNum;

                if ($thirdOrderNum > 100) {
                    $arr['fried100'][] = $thirdOrderNum;

                }
            }
        }

        if ($thirdOrderNum > 100) {
            $arr['kf100'][] = $thirdOrderNum;

            if ($categoryType == WaiMaiAnalysisModel::CATEGORY_TYPE_FRIED) {
                $arr['friedkf100'][] = $thirdOrderNum;
            }
        }
    }
}
